<p>  我们先来看代码：</p>
<pre><code class="language-JavaScript"><span class="code-block"><span class="hljs-keyword">function</span> <span class="hljs-title function_">fyShuffle</span>(<span class="hljs-params">arr</span>){
      <span class="hljs-comment">/*
      拷贝一个新的数组，原因是后面运行时不改变原数组，这样在需要的时候，
      就不会改变原数组了，如果需要改变原数组也只需要一个赋值就可以了
      */</span>
    <span class="hljs-keyword">let</span> _arr=arr.<span class="hljs-title function_">concat</span>();
    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i =_arr.<span class="hljs-property">length</span>-<span class="hljs-number">1</span>;i&gt;<span class="hljs-number">0</span>; i--){

        <span class="hljs-comment">/*
        Math.random()返回的值范围是[0.1),所以Math.floor(i*Math.random())取到的必然是数组下标 i 之前的元素
        */</span>
        <span class="hljs-keyword">let</span> ind=<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(i*<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>());

        <span class="hljs-comment">/*
        将数组下标i的元素和数组i之前取到的随机ind下标的元素调换位置
        */</span>
    [_arr[i], _arr[ind]] = [_arr[ind], _arr[i]];
    }
    <span class="hljs-keyword">return</span> _arr;
}</span>
</code><span class="copy-button">复制代码</span></pre>
<p>如上代码，我们可以看出算法过程：<br>  随机找到序列最后一个元素之前的元素，将随机找到的元素与序列最后一个元素交换位置；随机找到序列倒数第二个元素之前的元素，将随机找到的元素与倒数第二个元素交换位置。一直循环到序列第一个和第二个位置上的元素也做了交换为止。<br>  我们再来分析一下这个算法的特点：<br>一、我们知道一个有n个元素的序列排列组合方式有 n! 种方式，我们这个算法目的是为了打乱序列的排序，而且通过阅读代码，我们知道每个元素在经过调换后都不可能再继续待在原位置了，换句话说重新排列的序列有 (n-1)! 种排列方式。如果序列就两个元素，那么用这种算法重排后，它的新的排列方式就是确定的另一种了。但是这个算法能保证新的序列里面所有元素和排序之前的位置都不一样了，所以和之前的序列的组合方式一定不一样了。<br>二、每个元素的排列机会都是均等的，除了不可能再继续出现在自己原来的位置上，其他非自己的位置几率都是相等的。<br>三、算法时间复杂度为 O(n),是非常高效的算法了，算法的时间正比于乱序的数组长度。<br>四、算法本身的空间复杂度也是非常低的，在数组两个元素交换位置的时候，如果有一个临时变量用于保存值那也只是 O(1) 而已，我们这里复制了一个数组，主要是为了不想因为算法而改变了原数组而已，具体可以根据自己的需求需要来实现这个函数的。</p>
